package paim.wingchun.controller;


import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import javax.swing.JComponent;
import javax.swing.JLabel;

import org.hibernate.HibernateException;

import com.google.common.collect.Collections2;

import paim.wingchun.controller.eventos.DefaultExtraToolsEvent;
import paim.wingchun.controller.eventos.DefaultNavegadorEvent;
import paim.wingchun.controller.eventos.DefaultPanelButtonsEvent;
import paim.wingchun.controller.eventos.DefaultToolsEvent;
import paim.wingchun.model.Funcoes;
import paim.wingchun.model.modelos.Model;
import paim.wingchun.model.pojos.Erro;
import paim.wingchun.model.pojos.Pojo;
import paim.wingchun.view.AbstractSimpleView;
import paim.wingchun.view.AbstractView;
import paim.wingchun.view.Dialogs;
import paim.wingchun.view.componentes.WC_Label;

/** Controller para apresentacao de um objeto de visao, mas pode navegar por lista de objetos do mesmo tipo
 *
 * @author paim 21/01/2011 */
public abstract class AbstractSimpleController<P extends Pojo, V extends AbstractSimpleView<P>> extends
                AbstractController<P, V> implements MouseListener, ControllerListener {

    protected AbstractSimpleController() {
        super();
    }

    protected AbstractSimpleController(Class<P> pClass, Class<V> vClass, Class<? extends Model<P>> mClass) {
        super(pClass, vClass, mClass);
    }

    @Override
    protected final V c_instanciaVisao() throws Exception {
        return super.c_instanciaVisao();
        /* metodo deve acabar aqui nessa classe */
    }

    @Override
    protected final <M extends Model<P>> M c_instanciaModelo() throws Exception {
        return super.c_instanciaModelo();
        /* metodo deve acabar aqui nessa classe */
    }

    @Override
    protected void devolver(Object pojo) throws Exception {
        /* se tem sessoes diferentes */
        if ( !getSessao().equals(getPai().getSessao()) ) {
            P repojo = getModelo().get(getPai().getSessao(), ((Pojo) pojo).getId());
            getPai().getSessao().refresh(repojo);
            ((HasConstrutor) getPai()).recebeObjeto(repojo, getComponentPai());
        }
        else {
            /* se ambos controladores tem mesma sessao */
            ((HasConstrutor) getPai()).recebeObjeto(pojo, getComponentPai());
        }
    }

    @Override
    protected AbstractView.Estado c_confirmar(final Object objeto, List<Erro> erros) throws Exception {
        if ( !Pojo.class.isAssignableFrom(objeto.getClass()) )
            throw new Exception();

        AbstractView.Estado retorno = AbstractView.Estado.Alterado;

        Collection<Erro> impeditivos = Collections2.filter(erros, Funcoes.ERRO.impeditivos());

        /* os erros ja estao na visao, mas o metodo antesConfirmar pode ter alterado a lista de erros */
        getVisao().refreshErros(erros);

        if ( !impeditivos.isEmpty() ) {
            /* pergunta se devido aos erros quer cancelar ou nao */
            if ( Dialogs.showConfirmErros(this.getVisao(), impeditivos) ) {
                cancelar();
                retorno = AbstractView.Estado.Descartado;
            }
        }
        else {
            getModelo().saveOrUpdate(getSessao(), objeto);
            /* refresh visao */
            setObjeto(objeto);
            refreshErrosSlow(objeto);
            getVisao().setEditando(false);
            getVisao().getNavegador().refreshLastRegistro(((Pojo) objeto).getId());
            retorno = AbstractView.Estado.Confirmado;
        }
        return retorno;
    }

    @Override
    protected boolean c_cancelar(Object objeto) throws Exception {

        if ( !Pojo.class.isAssignableFrom(objeto.getClass()) )
            throw new Exception();

        Pojo pojo = (Pojo) objeto;

        if ( pojo.getId() != null ) {
            pojo = getModelo().reLoad(getSessao(), pojo);
            setObjeto(pojo);
            if ( pojo.getPendencia() > 0 )
                refreshErrosSlow(objeto);
        }
        else {
            getVisao().getNavegador().remove();
            if ( getVisao().getNavegador().isEmpty() )
                getVisao().setEmpty(true);
        }

        getVisao().setEditando(false);
        return true;

    }

    @Override
    protected boolean c_incluir(P novoPojo) {

        /* se estava vazio, entao seta nao vazio */
        if ( getVisao().isEmpty() )
            getVisao().setEmpty(false);

        // if ( getVisao().getNavegador().isEmpty() )
        // getVisao().setEmpty(false);

        getVisao().getNavegador().add();
        setObjeto(novoPojo);
        refreshErrosSlow(novoPojo);
        // getVisao().vCleanErros();
        getVisao().setEditando(true);

        return true;
    }

    @Override
    protected boolean c_excluir(Object objeto, List<Erro> erros) throws Exception {

        /* exclui o pojo */
        boolean excluido = super.c_excluir(objeto, erros);

        /* se nao excluiu, cai fora */
        if ( !excluido )
            return excluido;

        /* se realmente excluiu entao atualiza a visao */
        Long idAnterior = getVisao().getNavegador().remove();
        if ( getVisao().getNavegador().isEmpty() )
            getVisao().setEmpty(true);
        else {
            P objetoAnterior = getModelo().get(getSessao(), idAnterior);
            setObjeto(objetoAnterior);
        }
        return excluido;
    }

    @Override
    void addListeners() throws Exception {
        /* listener de eventos do wc */
        this.addListener(this);

        /* botoes confirmas, cancelar, .... */
        getVisao().getjPanelBotoes().addListener(new DefaultPanelButtonsEvent(this));
        /* toolbar */
        getVisao().getNavegador().addListener(new DefaultNavegadorEvent(this));
        getVisao().getNavegadorTools().addListener(new DefaultToolsEvent<P>(this));
        getVisao().getToolsExtra().addListener(new DefaultExtraToolsEvent<P>(this));

        /* adicionando listener para todos os labels de componentes que pode ter erros de validacao */
        for ( WC_Label label : getVisao().getMapLabel().values() ) {
            label.addMouseListener(this);
        }

    }

    protected List<Long> getIdsIniciais() throws HibernateException, Exception {
        return getModelo().ids(getSessao());
    }

    @Override
    protected Object cPopulaVisao() throws Exception {
        List<Long> ids = getIdsIniciais();

        if ( getPai() != null ) {
            // TODO posicionar no objeto do controlador pai......
            throw new Exception("TODO: implemente-me");
        }
        /* situacao normal caso nao tem controlador pai */

        if ( ids.isEmpty() ) {
            getVisao().setEmpty(true);
            return null;
        }
        else {
            getVisao().setEmpty(false);

            /* popula navegador com lista de ids */
            getVisao().getNavegador().addAll(ids);

            /* posiciona indice navegador */
            /* posiciona no ultimo sempre */
            getVisao().getNavegador().navega(Collections.max(ids));
            // /* posicionar no primeiro use ids.get(0); */

            Object id = getVisao().getNavegador().getRegistro();
            if ( id == null )
                return null;

            /* buscando objeto mostrado no navegador */
            Pojo pojo = getModelo().get(getSessao(), (Long) id);
            return pojo;

            // /* popula visao com um pojo */
            // setObjeto(pojo);

            // if ( pojo.getPendencia() > 0 )
            // refreshErrosSlow(pojo);

        }
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        /* verifica se nao esta parado */
        if ( !canDisparaEvento() )
            return;

        /* preciso pegar o erro que esta nesse componente, mas o componente com listener eh um JLabel */
        if ( (e.getSource() instanceof JLabel) && (((JComponent) e.getSource()).getParent() instanceof WC_Label) ) {

            WC_Label componente = (WC_Label) ((JComponent) e.getSource()).getParent();

            Erro erro = componente.getErro();
            /* se nao tem erro cai fora */
            if ( erro == null )
                return;

            Dialogs.showInternalErro(this.getVisao(), erro, componente.getIcon());
        }
    }

    @Override
    public void mousePressed(MouseEvent e) {
        /* verifica se nao esta parado */
        if ( !canDisparaEvento() )
            return;
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        /* verifica se nao esta parado */
        if ( !canDisparaEvento() )
            return;
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        /* verifica se nao esta parado */
        if ( !canDisparaEvento() )
            return;
    }

    @Override
    public void mouseExited(MouseEvent e) {
        /* verifica se nao esta parado */
        if ( !canDisparaEvento() )
            return;
    }

    @Override
    public void eventOccurred(WC_Event e) {
        switch ( (Eventos) e.evento() ) {
            case EDITANDO:
                getVisao().setEditando(true);
                System.out.println("EDITANDO");
            break;
            case ALTERANDO:
            default:
                refreshErrosSlow(e.getObjeto());
                ((Pojo) e.getObjeto()).setAlterado(true);
                getVisao().setEditando(true);
                System.out.println("ALTERAR");
            break;
        }
    }

    /** @author paim 25/05/2011 void */
    public void localizar() {
        new Localizar(this);
    }
}
